package com.wang.lottery.domain.strategy.service.draw;

import com.wang.lottery.common.Constants;
import com.wang.lottery.domain.activity.model.po.Award;
import com.wang.lottery.domain.strategy.model.aggregates.StrategyRich;
import com.wang.lottery.domain.strategy.model.po.Strategy;
import com.wang.lottery.domain.strategy.model.po.StrategyDetail;
import com.wang.lottery.domain.strategy.model.req.DrawReq;
import com.wang.lottery.domain.strategy.model.res.DrawResult;
import com.wang.lottery.domain.strategy.model.vo.AwardRateInfo;
import com.wang.lottery.domain.strategy.model.vo.DrawAwardInfo;
import com.wang.lottery.domain.strategy.service.algorithm.IDrawAlgorithm;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;

/**
 * @author wjh
 * @create 2023-11-01 14:42
 * 抽奖基础类，继承抽奖配置并校验和初始化奖品池数据
 */
@Slf4j
public abstract class AbstractDrawBase extends DrawStrategySupport implements IDrawExec{

    /**
     * 抽奖程序的入口
     *
     * @param drawReq 用户请求，
     * @return 响应奖品信息
     */
    @Override
    public DrawResult doDrawExec(DrawReq drawReq) {
        log.info("执行策略抽奖开始，strategyId：{}", drawReq.getStrategyId());
        Long strategyId = drawReq.getStrategyId();
        
        //获取抽奖策略配置数据，strategyRich包含了抽奖策略以及该策略下的所有详情信息（包括奖品id，奖品概率）
        StrategyRich strategyRich = super.queryStrategyRich(strategyId);
        Strategy strategy = strategyRich.getStrategy();
        
        //校验和初始化数据，主要是调用isExistRateTuple判断当前策略是否存在，如果不存在，调用initRateTuple初始化当前抽奖策略数据
        this.checkAndInitRateData(strategyId,strategy.getStrategyMode(),strategyRich.getStrategyDetailList());
        
        //获取不在抽奖范围内的列表，包括：奖品库存为空、风控策略、临时调整等
        List<String> excludeAwardIds = this.queryExcludeAwardIds(strategyId);
        
        //执行算法抽奖
        //根据策略方式（算法）抽奖，new ArrayList<>()，排除掉已经不能作为抽奖的奖品ID
        IDrawAlgorithm iDrawAlgorithm = super.drawAlgorithmMap.get(strategy.getStrategyMode());
        String awardId = this.drawAlgorithm(strategyId,iDrawAlgorithm,excludeAwardIds);
        
        //包装中奖结果
        return buildDrawResult(drawReq.getUserId(),strategyId,awardId,strategy);
    }

    /**
     * 获取不在抽奖范围内的列表，包括：奖品库存为空、风控策略、临时调整等，这类数据是含有业务逻辑的，所以需要由具体的实现方决定
     *
     * @param strategyId 策略ID
     * @return 排除的奖品ID集合
     */
    protected abstract List<String> queryExcludeAwardIds(Long strategyId);
    /**
     * 执行抽奖算法
     *
     * @param strategyId      策略ID
     * @param drawAlgorithm   抽奖算法模型
     * @param excludeAwardIds 排除的抽奖ID集合
     * @return 中奖奖品ID
     */
    protected abstract String drawAlgorithm(Long strategyId, IDrawAlgorithm drawAlgorithm, List<String> excludeAwardIds);
    /**
     * 校验和初始化奖品池数据
     * @param strategyId 策略id
     * @param strategyMode 策略算法类型
     * @param strategyDetailList 初始化奖品池
     */
    public void checkAndInitRateData(Long strategyId, Integer strategyMode, List<StrategyDetail> strategyDetailList) {
        //获取算法
        IDrawAlgorithm iDrawAlgorithm = super.drawAlgorithmMap.get(strategyMode);
        //判断当前策略是否已经存在
        boolean existRateTuple = iDrawAlgorithm.isExistRateTuple(strategyId);
        if (existRateTuple) {
            //如果存在就退出
            return;
        }
        //如果不存在就初始化这个抽奖策略
        List<AwardRateInfo> awardRateInfoList = new ArrayList<>(strategyDetailList.size());
        strategyDetailList.forEach(strategyDetail -> {
            //循环获取这个抽奖策略下的所有奖品封装到AwardRateInfo
            awardRateInfoList.add(new AwardRateInfo(strategyDetail.getAwardId(), strategyDetail.getAwardRate()));
        });
        //初始化
        iDrawAlgorithm.initRateTuple(strategyId,awardRateInfoList);
    }

    /**
     * 包装抽奖结果
     *
     * @param uId        用户ID
     * @param strategyId 策略ID
     * @param awardId    奖品ID，null 情况：并发抽奖情况下，库存临界值1 -> 0，会有用户中奖结果为 null
     * @param strategy
     * @return 中奖结果
     */
    private DrawResult buildDrawResult(String uId, Long strategyId, String awardId, Strategy strategy) {
        if (null == awardId) {
            log.info("执行策略抽奖完成【未中奖】，用户：{} 策略ID：{}", uId, strategyId);
            return new DrawResult(uId, strategyId, Constants.DrawState.FAIL.getCode());
        }
        DrawResult drawResult = DrawResult.builder().userId(uId).strategyId(strategyId).drawStatus(Constants.DrawState.SUCCESS.getCode()).build();
        Award award = super.queryAwardInfoByAwardId(awardId);
        drawResult.setDrawAwardInfo(DrawAwardInfo.builder().awardId(awardId)
                .awardName(award.getAwardName())
                .awardType(award.getAwardType())
                .awardContent(award.getAwardContent())
                .strategyMode(strategy.getStrategyMode())
                .grantType(strategy.getGrantType()).build());
        log.info("执行策略抽奖完成【已中奖】，用户：{} 策略ID：{} 奖品ID：{} 奖品名称：{}", uId, strategyId, awardId, award.getAwardName());
        return drawResult;
    }
}
